<?php
namespace core;
use Psr\Http\Message\RequestInterface;

/**
 * 路由处理
 * 路由规则限制   1."/"开始
 * Class RouteCollection
 * @package core
 */
Class RouteCollection
{
    protected $routes = []; // 路由存放
    protected $route_index = 0;  // 当前访问的路由
    public function getRoutes() // 获取所有路由
    {
        return $this->routes;
    }
    public $currGroup = []; // 当前组
    public function group($attributes = [],\Closure $callback)
    {
        $this->currGroup[] = $attributes;
        call_user_func($callback,$this);
        array_pop($this->currGroup);
    }
    // 增加/  如: GETUSER 改成 GET/USER
    protected function addSlash(& $uri)
    {
        return $uri[0] == '/' ? : $uri = '/'.$uri;
    }
    /**
     * 生成路由
     * @param string $route 路由
     * @param $param 路由动态参数
     * @param bool $complete 完整匹配 后面加$
     * @return string|string[]
     */
    function parse($route, &$param, $complete=true){
        //判断是否是动态路由
        if(strpos($route,'{') === false){
            return $route;
        }
        $isMatched = preg_match_all('/{[^}]+}/', $route, $matches);
        if($isMatched){
            $replace = [];
            foreach ($matches[0] as $match){
                $keyArr = explode(':',substr($match,1,-1));
                $param[] = $keyArr[0];
                $reg = '[^/]';
                if(isset($keyArr[1])){ //自定义的
                    $reg = str_replace(['<','>'],['{','}'],$keyArr[1]);
                }
                $endStr = str_split ($reg);
                if(!in_array(end($endStr),['?','+','*','}'])){
                    $reg .='+';
                }
                $replace[] = '('.$reg.')';
            }
            return str_replace($matches[0],$replace,$route).($complete?'$':'');
        }
        return $route;
    }


    /**
     * 解析动态路由
     * @param $url
     * @param $routes
     * @return mixed
     */
    function stringify($url,$routes){
        if(isset($routes[$url])){ //存在静态路由
            return  $routes[$url];
        }
        foreach ($routes as $routeReg=>$routeItem){
            if(strpos($routeReg,'(') === false){
                continue;
            }
            if($this->compare($url,$routeReg,$routeItem)){
                return $routeItem;
            }
        }
        return false;
    }

    function compare($url,$route,&$routerHandle){
        $isMatch = preg_match_all('#'.$route.'#', $url, $matches);
        //匹配成功
        if($isMatch){
            //去除第一个全匹配
            array_shift( $matches);
            //合并路由参数
            foreach ($routerHandle['paramkey'] as $ki => $kv){
                if(!isset($routerHandle['params'][$kv])){
                    $routerHandle['params'][$kv] = ($matches[$ki]??[''])[0];
                }
            }
            return true;
        }
        return false;
    }

    /**
     * 增加路由
     * @param $method
     * @param $uri
     * @param $uses
     * @param $param
     */
    public function addRoute($method, $uri, $uses, $params=[],$complete = true)
    {
        $prefix = ''; // 前缀
        $middleware = [];  // 中间件
        $namespace = ''; // 命名空间
        $this->addSlash($uri);
        foreach ($this->currGroup as $group){
            $prefix .= $group['prefix'] ?? false;
            if($prefix) // 如果有前缀
            {
                $this->addSlash($prefix);
            }

            $middleware = $group['middleware'] ?? []; // 合并组中间件
            $namespace .= $group['namespace'] ?? ''; // 拼接组的命名空间
        }
        $method = strtoupper($method); // 请求方式 转大写
        $uri = $prefix .$uri;
        $handel = [ // 路由存储结构  用 GET/USER   这种方式做索引 一次性就找到了
            'method' => $method,  // 请求类型
            'params'=>$params,//路由动态参数
            'paramkey'=>[],//路由参数
            'uri' => $uri,  // 请求url
            'action' => [
                'uses' => $uses,
                'middleware' => $middleware,
                'namespace' => $namespace
            ]
        ];
        $this->route_index = $this->parse($method . $uri,$handel['paramkey'],$complete);
        $this->routes[$this->route_index] = $handel;
    }

    public function get($uri,$uses, $params=[],$complete = true)
    {
        $this->addRoute('get',$uri,$uses, $params=[],$complete = true);
        return $this;
    }

    public function post($uri,$uses, $params=[],$complete = true)
    {
        $this->addRoute('post',$uri,$uses, $params=[],$complete = true);
        return $this;
    }
    public function middleware($class)
    {
        $this->routes[$this->route_index]['action']['middleware'][] = $class;
        return $this;
    }

// 获取当前访问的路由
    public function getCurrRoute()
    {
        $routes = $this->getRoutes();
        $route_index = $this->route_index;
        $handl = $this->stringify($route_index,$routes);
        if($handl!==false){
            return  $handl;
        }
        return  false;
    }
    // 更具request执行路由
    public function dispatch(RequestInterface $request)
    {

        $method = $request->getMethod();
        $uri = $request->getUri();
        $this->route_index = $method . $uri;
        $route = $this->getCurrRoute();

        if(! $route) // 找不到路由
        {
            return 404;
        }

        $middleware = $route['action']['middleware'] ?? [];
        $routerDispatch = $route['action']['uses'];
        if($route['params']){
            $request->withParam($route['params']);
        }
        if(! $route['action']['uses'] instanceof \Closure){ // 不是闭包 就是控制器了
            $action = $route['action'];
            $uses = explode('@',$action['uses']);
            $controller = $action['namespace'].'\\'.$uses[0]; // 控制器
            $method = $uses[1]; // 执行的方法

            $controllerInstance = new $controller;
            $middleware = array_merge($middleware,$controllerInstance->getMiddleware()); // 合并控制器中间件
            $routerDispatch = function ($request) use($route, $controllerInstance, $method){
                return $controllerInstance->callAction($method,[ $request ]);
            };
        }

        return \App::getContainer()->get('pipeline')->create()->setClass(
            $middleware
        )->run($routerDispatch)($request);

    }
}